#pragma once

#ifndef __BIPL_SYMBOLTABLE_H__
#define __BIPL_SYMBOLTABLE_H__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <list.h>

#define RUNS 1000000
#define BOUND 1000000

struct myEntry {
	char* name;
	char* type;
	int params;	// -1 for variables
	struct myEntry* next;
};

struct Env {
	struct myEntry* top;
	struct Env* prev;
};

void print_Env (char *pad, struct Env *self){
	struct myEntry * entry = self->top;
	int i = 1;
	
	while (entry != NULL) {
		printf("%s%i) %s %s %i\n", pad, i, entry->name,	entry->type, entry->params);
		i++;
		entry = entry->next;
	}
}

void print_Env_Stack (char *pad, struct Env *self){
	struct Env * e = self;
	int i = 1;

	printf("%s--------------------------\n", pad);
	printf("%s|     Stack Contents     |\n", pad);
	printf("%s--------------------------\n", pad);
	while (e != NULL) {
		printf("%sEnv %i\n", pad, i);
		print_Env(pad, e);
		printf( "\n");
		i++;
		e = e->prev;
	}
	printf("%s--------------------------\n", pad);
}

void pretty_print_Env (char *pad, struct Env *self){
	struct myEntry * entry = self->top;
	int i = 1;
	while (entry != NULL) {
		if (strlen(entry->name)>19)
			printf("%s|%i) %.18s~\t%s\t%i\t|\n", pad, i, entry->name, entry->type, entry->params);
		else if (strlen(entry->name)>11)
			printf("%s|%i) %s\t%s\t%i\t|\n", pad, i, entry->name, entry->type, entry->params);
		else if (strlen(entry->name)>3)
			printf("%s|%i) %s\t\t%s\t%i\t|\n", pad, i, entry->name, entry->type, entry->params);
		else
			printf("%s|%i) %s\t\t\t%s\t%i\t|\n", pad, i, entry->name, entry->type, entry->params);
		i++;
		entry = entry->next;
	}
}

void pretty_print_Env_Stack (char *pad, struct Env *self){
	struct Env * e = self;
	int i = 1;
	printf("%s-------------------------------------------------\n", pad);
	printf("%s|                 Stack Contents                |\n", pad);
	printf("%s-------------------------------------------------\n", pad);
	while (e != NULL) {
		printf("%s|Env %i\t\t\t\t\t\t|\n", pad, i);
		pretty_print_Env(pad, e);
		i++;
		e = e->prev;
		printf("%s-------------------------------------------------\n", pad);
	}
}

void init_Env (struct Env * self, struct Env * p) {
	self->top = NULL;
	self->prev = p;

}

void put_Env (struct Env* self, struct myEntry* entry){
	if (self->top == NULL) {
		self->top = entry;
	} else { 
		entry->next = self->top;
		self->top = entry;
	}
}


struct myEntry* get_Env (struct Env *self, char* s){
	struct Env * e = self;
	while (e != NULL) {
		struct myEntry * entry = e->top;
		while (entry != NULL) {
			if (strcmp(entry->name, s) == 0)
				return entry;
			entry = entry->next;
		}
		e = e->prev;
	}
	return NULL;
}

struct myEntry* check_Env (struct Env *self, char* s){
	struct myEntry * entry = self->top;
	while (entry != NULL) {
		if (strcmp(entry->name, s) == 0)
			return entry;
		entry = entry->next;
	}
	return NULL;
}

void free_Env (struct Env *self){
	if (self != NULL) {
		struct myEntry * entry = self->top;
		while (entry != NULL) {
			struct myEntry * next = entry->next;
			free(entry->name);
			free(entry);
			entry = next;
		}
		free(self);
	}
}


/* Functions directly called by parser. */

extern struct Env * top;

struct myEntry* add_Entry(char *name, char *type, int params, YYLTYPE t) {
	struct myEntry* T = check_Env(top, name);
	

	if (strcmp("_dollar_sign", name)==0) {
		lyyerror(t, "%s can not be declared", name);
		T = NULL;
	} else if (T!=NULL) {
		lyyerror(t, "%s redefinition", name);
		T = NULL;
	} else {
		struct myEntry* s = (struct myEntry*) malloc(sizeof (struct myEntry));
		s->name = name;
		s->type = type;
		s->params = params;
		s->next = NULL;
		put_Env(top, s);
		T = s;
		printf("Adding Entry: %s %s %i\n", type, name, params);
	}
	//pretty_print_Env_Stack("\t", top);
	return T;
}

void manual_Add_Entry(char *name, char *type, int params) {
	struct myEntry* s = (struct myEntry*) malloc(sizeof (struct myEntry));
	s->name = strdup(name);
	s->type = type;
	s->params = params;
	s->next = NULL;
	put_Env(top, s);
}

void check_Entry(char *name, int params, YYLTYPE t) {
	struct myEntry* T = get_Env(top, name);
	if (T==NULL) {
		if (params == -1){
			lyyerror(t, "%s: undeclared variable", name);
		}
		else{
			lyyerror(t, "%s: undeclared function", name);
		}
	} else if ((params == -1) && (T->params != -1)) {
		lyyerror(t, "%s: declared as function", name);
	} else if ((params != -1) && (T->params == -1)) {
		lyyerror(t, "%s: declared as variable", name);
	} else if (params != T->params) {
		lyyerror(t, "%s: %i parameters but expected %i", name, params, T->params);
	}else{
		//printf("%s is found of %s with %i parameter(s)\n", name, T->type, T->params);
	}
}

void new_Env() {
	struct Env* saved = top;
	top = (struct Env*) malloc(sizeof (struct Env)); 
	init_Env(top, saved);
}

void prev_Env() {
	struct Env* saved = top->prev;
	free_Env(top);
	top = saved;
}

#endif
